/**
 * 配置中心
 * @author zj
 * @date 2024/08/22
 */
import {
  AfterHandler,
  AfterHandlerManger,
  AnyRelationHandler,
  AnyTypeHandler,
  BaseRelationHandler,
  BeforeHandler,
  BeforeHandlerManger,
  DefaultAfterHandlerManger,
  DefaultBeforeHandlerManger,
  DefaultPropHandler,
  DefaultPropHandlerManger,
  DefaultRelationHandlerManger,
  DefaultTypeHandler,
  ExtendRelationHandler,
  KeyValueRelationHandler,
  MyselfRelationHandler,
  PropHandler,
  PropHandlerManger,
  RelationHandler,
  RelationHandlerManger,
  RelationTypeHandler,
  TypeHandler
} from './handler.ts';
import {getDefaultByNull, isNotNull} from './utils.ts';
import {NameConsts, RelationNames} from './consts.ts';

export type RelationOption = {
  slotName?: string,
  relation: string,
  propName?: string,
  propValue?: any
  componentRef: string,
  componentPrefix?: string,
  componentBinds?: string,
  handleProp?: boolean,
  inherits?: Record<string, string>,
  typeHandlers?: string[],
  propHandlers?: string[],
  beforeHandlers?: string[],
  afterHandlers?: string[],
  relations?: RelationOption[],
  extendAttrs?: any
}

export type ComponentOption = {
  name: string,
  type: string,
  props?: string[],
  alias?: Record<string, string>,
  prefix?: string,
  binds?: string,
  childRef?: string,
  inherits?: Record<string, string>,
  ignoreAttrNames?: string[],
  typeHandlers?: string[],
  propHandlers?: string[],
  beforeHandlers?: string[],
  afterHandlers?: string[],
  relationHandlers?: string[],
  relation?: RelationOption
};

export type TypeHandlerOption = {
  name: string,
  handler?: TypeHandler
};

export type PropHandlerOption = {
  name: string,
  handler?: PropHandler
};

export type BeforeHandlerOption = {
  name: string,
  handler?: BeforeHandler
};

export type AfterHandlerOption = {
  name: string,
  handler?: AfterHandler
};

export type RelationHandlerOption = {
  name: string,
  handler?: RelationHandler
};

export type ConfigData = {
  valueKey?: string,
  updateKey?: string,
  disabledKey?: string
};

export type Options = {
  configs?: ConfigData,
  components?: ComponentOption[]
  typeHandlers?: TypeHandlerOption[]
  propHandlers?: PropHandlerOption[]
  beforeHandlers?: BeforeHandlerOption[]
  afterHandlers?: AfterHandlerOption[]
  relationHandlers?: RelationHandlerOption[]
};

export type ResolveData = {
  nameMappers: Record<string, string>,
  refMappers: Record<string, ComponentOption>,
  configs: ConfigData;
  typeHandlers: Record<string, TypeHandler>;
  propHandlers: Record<string, PropHandler>;
  beforeHandlers: Record<string, BeforeHandler>;
  afterHandlers: Record<string, AfterHandler>;
  relationHandlers: Record<string, RelationHandler>;
}

export interface Configure {

  isMatchAny(type: string);

  getHandlerByType(type: string): TypeHandler | undefined;

  getAnyHandler(): TypeHandler | undefined;

  getValueKey(): string;

  getUpdateKey(): string;

  getDisabledKey(): string;

}

export class ResolveConfigure implements Configure {
  private _configs: ConfigData;
  private _typeHandlers: Record<string, TypeHandler>;
  private _propHandlers: Record<string, PropHandler>;
  private _beforeHandlers: Record<string, BeforeHandler>;
  private _afterHandlers: Record<string, AfterHandler>;
  private _relationHandlers: Record<string, RelationHandler>;

  constructor() {
  }

  setOptions(options: Options) {
    const resolveData: ResolveData = {
      nameMappers: {},
      refMappers: {},
      configs: {},
      typeHandlers: {},
      propHandlers: {},
      beforeHandlers: {},
      afterHandlers: {},
      relationHandlers: {},
    };
    this.resolveConfigs(resolveData, options.configs);
    this.resolveMappers(resolveData, options.components);
    this.resolveTypeHandlers(resolveData, options.typeHandlers);
    this.resolvePropHandlerOption(resolveData, options.propHandlers);
    this.resolveBeforeHandlerOption(resolveData, options.beforeHandlers);
    this.resolveAfterHandlerOption(resolveData, options.afterHandlers);
    this.resolveRelationHandlerOption(resolveData, options.relationHandlers);
    this.resolveComponents(resolveData, options.components);
    this._configs = resolveData.configs;
    this._typeHandlers = resolveData.typeHandlers;
    this._propHandlers = resolveData.propHandlers;
    this._beforeHandlers = resolveData.beforeHandlers;
    this._afterHandlers = resolveData.afterHandlers;
    this._relationHandlers = resolveData.relationHandlers;
    console.log('render resolve >>> configure=', this);
  }

  resolveMappers(resolveData: ResolveData, components?: ComponentOption[]): void {
    if (components && components.length > 0) {
      for (let i = 0; i < components.length; i++) {
        const component = components[i];
        if (component) {
          resolveData.nameMappers[component.name] = component.type;
          resolveData.refMappers[component.name] = component;
        }
      }
    }
  }

  resolveTypeHandlers(resolveData: ResolveData, handlers?: TypeHandlerOption[]): void {
    // 设置默认handlers
    if (handlers && handlers.length > 0) {
      for (let i = 0; i < handlers.length; i++) {
        const handler = handlers[i];
        if (handler && handler.name) {
          if (handler.handler) {
            resolveData.typeHandlers[handler.name] = handler.handler;
          }
        }
      }
    }
  }

  resolvePropHandlerOption(resolveData: ResolveData, handlers?: PropHandlerOption[]): void {
    // 设置默认handlers
    if (handlers && handlers.length > 0) {
      for (let i = 0; i < handlers.length; i++) {
        const handler = handlers[i];
        if (handler && handler.name) {
          if (handler.handler) {
            resolveData.propHandlers[handler.name] = handler.handler;
          }
        }
      }
    }
  }

  resolveBeforeHandlerOption(resolveData: ResolveData, handlers?: BeforeHandlerOption[]): void {
    // 设置默认handlers
    if (handlers && handlers.length > 0) {
      for (let i = 0; i < handlers.length; i++) {
        const handler = handlers[i];
        if (handler && handler.name) {
          if (handler.handler) {
            resolveData.beforeHandlers[handler.name] = handler.handler;
          }
        }
      }
    }
  }

  resolveAfterHandlerOption(resolveData: ResolveData, handlers?: AfterHandlerOption[]): void {
    // 设置默认handlers
    if (handlers && handlers.length > 0) {
      for (let i = 0; i < handlers.length; i++) {
        const handler = handlers[i];
        if (handler && handler.name) {
          if (handler.handler) {
            resolveData.afterHandlers[handler.name] = handler.handler;
          }
        }
      }
    }
  }

  resolveRelationHandlerOption(resolveData: ResolveData, handlers?: RelationHandlerOption[]): void {
    // 设置默认handlers
    if (handlers && handlers.length > 0) {
      for (let i = 0; i < handlers.length; i++) {
        const handler = handlers[i];
        if (handler && handler.name) {
          if (handler.handler) {
            resolveData.relationHandlers[handler.name] = handler.handler;
          }
        }
      }
    }
  }

  resolveComponents(resolveData: ResolveData, components?: ComponentOption[]): void {
    if (components && components.length > 0) {
      for (let i = 0; i < components.length; i++) {
        const component = components[i];
        if (component) {
          if (component.type === '*') {
            const typeHandler = new AnyTypeHandler();
            typeHandler.setName(component.name);
            typeHandler.setPropHandlerManger(this.createPropMangerByComponent(resolveData, component));
            typeHandler.setAfterHandlerManger(this.createAfterMangerByComponent(resolveData, component));
            typeHandler.setBeforeHandlerManger(this.createBeforeMangerByComponent(resolveData, component));
            typeHandler.setIgnoreAttrNames(component.ignoreAttrNames);
            typeHandler.setChildType(resolveData.nameMappers[component.childRef]);
            resolveData.typeHandlers[component.type] = typeHandler;
          } else {
            const relationManger = this.createRelationManger(resolveData, component);
            const isRelation = relationManger !== undefined;
            if (isRelation) {
              const typeHandler = new RelationTypeHandler(component.type);
              typeHandler.setName(component.name);
              typeHandler.setPropHandlerManger(this.createPropMangerByComponent(resolveData, component));
              typeHandler.setAfterHandlerManger(this.createAfterMangerByComponent(resolveData, component));
              typeHandler.setBeforeHandlerManger(this.createBeforeMangerByComponent(resolveData, component));
              typeHandler.setRelationHandlerManger(relationManger);
              typeHandler.setIgnoreAttrNames(component.ignoreAttrNames);
              typeHandler.setChildType(resolveData.nameMappers[component.childRef]);
              resolveData.typeHandlers[component.type] = typeHandler;
            } else {
              const typeHandler = new DefaultTypeHandler(component.type);
              typeHandler.setName(component.name);
              typeHandler.setPropHandlerManger(this.createPropMangerByComponent(resolveData, component));
              typeHandler.setAfterHandlerManger(this.createAfterMangerByComponent(resolveData, component));
              typeHandler.setBeforeHandlerManger(this.createBeforeMangerByComponent(resolveData, component));
              typeHandler.setIgnoreAttrNames(component.ignoreAttrNames);
              typeHandler.setChildType(resolveData.nameMappers[component.childRef]);
              resolveData.typeHandlers[component.type] = typeHandler;
            }
          }
        }
      }
    }
  }

  resolveProps(props: string[] | undefined, alias: Record<string, string> | undefined): Record<string, string> | undefined {
    let isMatch = false;
    const result: Record<string, string> = {};
    if (props && props.length > 0) {
      for (let i = 0; i < props.length; i++) {
        const prop = props[i];
        if (prop) {
          isMatch = true;
          result[prop] = prop;
        }
      }
    }
    if (alias) {
      for (const key in alias) {
        if (alias.hasOwnProperty(key)) {
          isMatch = true;
          result[key] = alias[key];
        }
      }
    }
    if (isMatch) {
      return result;
    }
  }

  createPropMangerByComponent(resolveData: ResolveData, component: ComponentOption): PropHandlerManger {
    const propHandlerManger = new DefaultPropHandlerManger();
    const prefix = component.prefix;
    const props = this.resolveProps(component.props, component.alias);
    if (props || prefix) {
      propHandlerManger.addPropHandler(new DefaultPropHandler(props, prefix));
    }
    const propHandlers = component.propHandlers;
    if (propHandlers && propHandlers.length > 0) {
      for (let i = 0; i < propHandlers.length; i++) {
        propHandlerManger.addPropHandler(resolveData.propHandlers[propHandlers[i]]);
      }
    }
    propHandlerManger.setInherits(component.inherits);
    propHandlerManger.setBindsName(component.binds);
    propHandlerManger.setLoadingKey(NameConsts.loadingKey);
    propHandlerManger.setDisabledKey(NameConsts.disabledKey);
    return propHandlerManger;
  }

  createBeforeMangerByComponent(resolveData: ResolveData, component: ComponentOption): BeforeHandlerManger {
    const beforeHandlerManger = new DefaultBeforeHandlerManger();
    const beforeHandlers = component.beforeHandlers;
    if (beforeHandlers && beforeHandlers.length > 0) {
      for (let i = 0; i < beforeHandlers.length; i++) {
        beforeHandlerManger.addBeforeHandler(resolveData.beforeHandlers[beforeHandlers[i]]);
      }
    }
    return beforeHandlerManger;
  }

  createAfterMangerByComponent(resolveData: ResolveData, component: ComponentOption): AfterHandlerManger {
    const afterHandlerManger = new DefaultAfterHandlerManger();
    const afterHandlers = component.afterHandlers;
    if (afterHandlers && afterHandlers.length > 0) {
      for (let i = 0; i < afterHandlers.length; i++) {
        afterHandlerManger.addAfterHandler(resolveData.afterHandlers[afterHandlers[i]]);
      }
    }
    return afterHandlerManger;
  }

  createRelationManger(resolveData: ResolveData, component: ComponentOption): RelationHandlerManger | undefined {
    const relationHandlerManger = new DefaultRelationHandlerManger();
    const myselfRelationHandler = this.createMyselfRelationHandler(resolveData, component);
    if (myselfRelationHandler) {
      const relationHandlers = this.createRelationHandlers(resolveData, component.relation?.relations);
      if (relationHandlers && relationHandlers.length > 0) {
        relationHandlerManger.setMyselfHandler(myselfRelationHandler);
        relationHandlerManger.setRelationHandlers(relationHandlers);
        return relationHandlerManger;
      }
    }
  }

  createRelationHandlers(resolveData: ResolveData, relations?: RelationOption[]): RelationHandler[] | undefined {
    const relationHandlers: RelationHandler[] = [];
    if (relations && relations.length > 0) {
      for (let i = 0; i < relations.length; i++) {
        const relation = relations[i];
        if (relation) {
          const relationHandler = this.createRelationHandler(resolveData, relation);
          if (relationHandler) {
            let nextRelations = relation.relations;
            if (!nextRelations) {
              const component = resolveData.refMappers[relation.componentRef];
              nextRelations = component?.relation?.relations;
            }
            if (relationHandler instanceof BaseRelationHandler) {
              relationHandler.setChildrenRelations(this.createRelationHandlers(resolveData, nextRelations));
            }
            relationHandlers.push(relationHandler);
          }
        }
      }
    }
    if (relationHandlers.length > 0) {
      return relationHandlers;
    }
  }

  createRelationHandler(resolveData: ResolveData, relation: RelationOption): RelationHandler | undefined {
    if (!relation.relation) {
      return;
    }
    if (!relation.componentRef) {
      return;
    }
    let relationHandler: RelationHandler;
    const slotName = relation.slotName || 'default';
    if (relation.relation === RelationNames.keyValueRelationHandler) {

      if (!isNotNull(relation.propName) || !isNotNull(relation.propValue)) {
        throw new Error('propName and propValue is must');
      }
      const type = resolveData.nameMappers[relation.componentRef];
      if (!isNotNull(type)) {
        throw new Error('wrong type reference');
      }
      relationHandler = new KeyValueRelationHandler(<string>type);
      if (relationHandler instanceof KeyValueRelationHandler) {
        relationHandler.setPropName(relation.propName);
        relationHandler.setPropValue(relation.propValue);
        relationHandler.setSlotName(slotName);
      }

    } else if (relation.relation === RelationNames.matchAllRelationHandler) {

      relationHandler = new AnyRelationHandler();

    } else {
      relationHandler = this._relationHandlers[relation.relation];

      if (!relationHandler) {
        throw new Error('relation-handler undefined');
      }

      if (relationHandler instanceof BaseRelationHandler) {
        relationHandler.setSlotName(slotName);
      }
      if (relationHandler instanceof ExtendRelationHandler) {
        relationHandler.setExtendAttrs(relation.extendAttrs);
      }
    }

    if (relationHandler instanceof BaseRelationHandler) {
      const component = resolveData.refMappers[relation.componentRef];
      const relationComponent = this.resolveRelationComponent(resolveData, component, relation);
      relationHandler.setPropHandlerManger(this.createPropMangerByComponent(resolveData, relationComponent));
      relationHandler.setAfterHandlerManger(this.createAfterMangerByComponent(resolveData, relationComponent));
      relationHandler.setBeforeHandlerManger(this.createBeforeMangerByComponent(resolveData, relationComponent));
      relationHandler.setChildType(resolveData.nameMappers[relationComponent.childRef]);
      relationHandler.setHandleProp(relation.handleProp);
    }

    return relationHandler;
  }

  createMyselfRelationHandler(resolveData: ResolveData, component: ComponentOption): RelationHandler | undefined {
    const relation = component.relation;
    if (!relation) {
      return;
    }
    let relationHandler: RelationHandler;
    if (relation.relation) {
      relationHandler = this._relationHandlers[relation.relation];

      if (!relationHandler) {
        throw new Error('relation-handler undefined');
      }

      if (relationHandler instanceof ExtendRelationHandler) {
        relationHandler.setExtendAttrs(relation.extendAttrs);
      }
    } else {
      relationHandler = new MyselfRelationHandler(component.type);
    }

    if (relationHandler instanceof BaseRelationHandler) {
      const relationComponent = this.resolveRelationComponent(resolveData, component, relation);
      relationHandler.setPropHandlerManger(this.createPropMangerByComponent(resolveData, relationComponent));
      relationHandler.setAfterHandlerManger(this.createAfterMangerByComponent(resolveData, relationComponent));
      relationHandler.setBeforeHandlerManger(this.createBeforeMangerByComponent(resolveData, relationComponent));
      relationHandler.setChildType(resolveData.nameMappers[relationComponent.childRef]);
      relationHandler.setHandleProp(relation.handleProp);
    }

    return relationHandler;
  }

  resolveRelationComponent(resolveData: ResolveData, component: ComponentOption, relation: RelationOption): ComponentOption {
    return {
      name: component.name,
      type: component.type,
      props: component.props,
      alias: component.alias,
      childRef: component.childRef,
      inherits: getDefaultByNull(relation.inherits, component.inherits),
      prefix: getDefaultByNull(relation.componentPrefix, component.relation?.componentPrefix, component.prefix),
      binds: getDefaultByNull(relation.componentBinds, component.relation?.componentBinds, component.binds),
      typeHandlers: getDefaultByNull(relation.typeHandlers, component.relation?.typeHandlers, component.typeHandlers),
      propHandlers: getDefaultByNull(relation.propHandlers, component.relation?.propHandlers, component.propHandlers),
      beforeHandlers: getDefaultByNull(relation.beforeHandlers, component.relation?.beforeHandlers, component.beforeHandlers),
      afterHandlers: getDefaultByNull(relation.afterHandlers, component.relation?.afterHandlers, component.afterHandlers)
    };
  }

  resolveConfigs(resolveData: ResolveData, opts?: ConfigData): void {
    resolveData.configs.valueKey = opts?.valueKey;
    resolveData.configs.updateKey = opts?.updateKey;
    resolveData.configs.disabledKey = opts?.disabledKey;
  }

  isMatchAny(type: string) {
    return type === '*';
  }

  getHandlerByType(type: string): TypeHandler | undefined {
    return this._typeHandlers[type];
  }

  getAnyHandler(): TypeHandler | undefined {
    return this._typeHandlers['*'];
  }

  getValueKey(): string {
    return this._configs?.valueKey || 'value';
  }

  getUpdateKey(): string {
    return this._configs?.updateKey || 'onUpdate:value';
  }

  getDisabledKey(): string {
    return this._configs?.disabledKey || 'disabled';
  }

}

export default new ResolveConfigure();
